package strauss.flatland.control;

import java.util.List;

import strauss.flatland.geometry.Calculator;
import strauss.flatland.geometry.SimpleRectangle;
import strauss.flatland.model.FlatLandModel;
import strauss.flatland.things.IThing;
import strauss.flatland.things.Line;
import strauss.flatland.things.geometry.Collision;
import strauss.flatland.things.geometry.GeometricVector;
import strauss.flatland.things.geometry.VectorMath;
import strauss.flatland.things.geometry.Vertex;

public class FlatLandControl implements Runnable {
	private boolean running;
	private Thread thread;

	private FlatLandModel model;

	public FlatLandControl(FlatLandModel model) {
		this.model = model;
		thread = new Thread(this);
	}
	
	private boolean isAOnB(IThing a, IThing b) {
		// very silly collision code
		boolean collision = false;
		List<Vertex> tmpCoord = a.getCoordinates();
		
		// create a bounding rectangle for a
		SimpleRectangle rectA = null;
		for (Vertex vertex : tmpCoord) {
			rectA=Calculator.createBoundingRectangle(rectA, vertex);
			Vertex tmpVertex = new Vertex(vertex);
			tmpVertex.translate(a.getGeometricVector());
			rectA=Calculator.createBoundingRectangle(rectA, vertex);
		}
		// create a bounding rectangle for b
		SimpleRectangle rectB = null;
		tmpCoord = b.getCoordinates();
		for (Vertex vertex : tmpCoord) {
			rectB=Calculator.createBoundingRectangle(rectB, vertex);
			Vertex tmpVertex = new Vertex(vertex);
			tmpVertex.translate(b.getGeometricVector());
			rectB=Calculator.createBoundingRectangle(rectB, vertex);
		}
		
		if (Calculator.isPointInRectangle(rectA.getTop(), rectA.getLeft(), rectB)) {
			collision=true;
		} else if (Calculator.isPointInRectangle(rectA.getTop(), rectA.getRight(), rectB)) {
			collision=true;
		} else if (Calculator.isPointInRectangle(rectA.getBottom(), rectA.getLeft(), rectB)) {
			collision=true;
		} else if (Calculator.isPointInRectangle(rectA.getBottom(), rectA.getRight(), rectB)) {
			collision=true;
		}
		
		// do an vertices go over each other?
		
		return collision;
	}
	
	/**
	 * @deprecated move this method somewhere else
	 * @param a
	 * @param b
	 * @return
	 */
	public boolean detectCollision(IThing a, IThing b) {
		boolean collision = false;
		double ax1 = a.getBounds2D().getX();
		double ay1 = a.getBounds2D().getY();
		double ax2 = ax1+a.getBounds2D().getWidth();
		double ay2 = ay1+a.getBounds2D().getHeight();
		boolean ainfinite = ax1==ax2;
		double am = ax1==ax2?0:(ay2-ay1)/(ax2-ax1);
		
		double bx1 = b.getBounds2D().getX();
		double by1 = b.getBounds2D().getY();
		double bx2 = bx1+b.getBounds2D().getWidth();
		double by2 = by1+b.getBounds2D().getHeight();
		boolean binfinte = bx1==bx2;
		double bm = bx1==bx2?0:(by2-by1)/(bx2-bx1);
		
		if (binfinte) {
			// there are an infinite amount of possible intersection
			// points on the y axis of line b, because the line goes straight up
			// (x stays the same) 
			// if am==0, then y stays the same on line a
			// otherwise, we need to find where line a intersects
			// with line b
			//
			//    -
			//    |line b (x stays same)
			//    |
			// |--|----| linea (y stays same) 
			//    |
			//    -
			// yb = anything between yb1 and yb2
			// am==0 then
			// ya = ya1 or ya2
			// am!= then
			//
			if (am==0) {
				double ay=ay1;
				double bx=bx1;
				if (by1<=ay&&by2>=ay || by1>=ay&&by2<=ay) {
					if (ax1<=bx&&ax2>=bx || ax1>=bx&&ax2<=bx) {
						collision=true;
					}
				}
			} else {
				double xintersect = bx1;
				double yintersect = am*(xintersect-ax1)+ay1;
				collision = checkIntersect(collision, ax1, ay1, ax2, ay2, bx1, by1, bx2, by2, xintersect, yintersect);
			}
		} else if (ainfinite) {
			if (bm==0) {
				double by=by1;
				double ax=ax1;
				if (ay1<=by&&ay2>=by || ay1>=by&&ay2<=by) {
					if (bx1<=ax&&bx2>=ax || bx1>=ax&&bx2<=ax) {
						collision=true;
					}
				}
			} else {
				double xintersect = ax1;
				double yintersect = bm*(xintersect-bx1)+by1;
				collision = checkIntersect(collision, ax1, ay1, ax2, ay2, bx1, by1, bx2, by2, xintersect, yintersect);
			}
		} else {
			double xintersect = 0;
			double yintersect = 0;
			if (am==0 && bm==0) {
				// head on collision
				// y remains constant for line a
				// we have a collision, if b ever has a y within a certain range
				if (ay1==by1) {
					if (ax1>=bx1 && ax1<=bx2 || ax2>=bx1&&ax2<=bx2) {
						collision=true;
					}
				}
			} 
			else {
				if (am-bm==0) {
					// the vectors are parralel to each other!
					// they have the same angle
					// there fore there is an infinite amount of possible
					// xintercepts
					// do the lines ever have the same point?
					// we take a point on line 1, and see if it's the same on line two
					// y = mx + c
					// c = y - mx
					double cb = by1-bm*bx1;
					// ax1 is a point on line 1, so if the y coordinate
					// we get is y = mx + c = ay1 - the the lines will intersect,
					// and we need only check the range
					double tmpY = bm*ax1 + cb;
					
					if (tmpY==ay1) {
						// we are going to intersect, check the range
						if (ax1>=bx1 && ax1<=bx2) {
							if (ay1>=by1 && ay1<=by2) {
								collision=true;
							}
						}
					}
					
					//double ay=am*(ax1-ax2)+ay2;
					//double by=bm*(bx1-bx2)+bx2;
					//if (ay-by!=0) {
						//xintersect=ay-by;
						//yintersect = bm * (xintersect-bx1)+by1;
						//collision = checkIntersect(collision, ax1, ay1, ax2, ay2, bx1, by1, bx2, by2, xintersect, yintersect);
					//}
				} else {
					xintersect = (am*ax1-bm*bx1 + by1 - ay1)/(am-bm);
					yintersect = bm * (xintersect-bx1)+by1;
					collision = checkIntersect(collision, ax1, ay1, ax2, ay2, bx1, by1, bx2, by2, xintersect, yintersect);
				}
				
			}
		}
		return collision;
	}

	private boolean checkIntersect(boolean collision, double ax1, double ay1, double ax2, double ay2, double bx1, double by1, double bx2, double by2, double xintersect, double yintersect) {
		if (xintersect>=ax1&&xintersect<=ax2 || xintersect<=ax1&&xintersect>=ax2) {
			if (yintersect>=ay1&&yintersect<=ay2 || yintersect<=ay1&&yintersect>=ay2) {
				if (xintersect>=bx1&&xintersect<=bx2 || xintersect<=bx1&&xintersect>=bx2) {
					if (yintersect>=by1&&yintersect<=by2 || yintersect<=by1&&yintersect>=by2) {
						collision=true;
					}
				}
			}
		}
		return collision;
	}
	
	private double vectorLength(GeometricVector v) {
		return (v.getXD()*v.getXD() + v.getYD()+v.getYD())/2;
	}

	public void run() {
		while (isRunning()) {

			List<IThing> things = model.getThings();
			for (int i=0;i<things.size();++i) {
				IThing a=things.get(i);
				for (int i2=i+1;i2<things.size();++i2) {
					IThing b=things.get(i2);
					// is thing going to collide with otherthing?
					// lots of thinking needed to make this exact.
					// e.g. if we have three objects that are going to collide
					// within a given space, we have to calculate them in order
					// and at the points at which they will collide
					if (detectCollision(a, b)) {
						if (!(a instanceof Line && b instanceof Line)) {
							Collision.doCollision2(a, b, null);
						}
					}
				}
				a.update();
				//thing.update(model.getThings());
			}
			model.incrementTime();
			// signal to who cares, that we've done our update
			model.getSemaphore().release();
			Thread.yield(); // yield to other threads
			try {
				// TODO: think about this sleep thing.... in true sim 
				// mode, we want to run as fast as possible... but in
				// game mode, the universe will have a fixed step rate
				Thread.sleep(20);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

	}

	private boolean isRunning() {
		return running;
	}

	public void startUniverse() {
		if (!running) {
			running=true;
			thread.start();
		}
	}
	
	public void terminateUniverse() {
		running=false;
		try {
			thread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}
